﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: ContinuousUniformDistribution.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2012-09-06 11:39 AM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Collections.Generic;
using System.Linq;

namespace BOL.Maths.Distributions
{
    /// <summary>
    /// Represents a continuous uniform statistical distribution giving values between min and max.
    /// <see>Mathematica: ref/UniformDistribution</see>
    /// <see>http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)</see>
    /// </summary>
    public sealed class ContinuousUniformDistribution : IUnivariateDistribution<double>, IEquatable<ContinuousUniformDistribution>
    {
        #region Private variables

        private double _min;
        private double _max;
        private double _range;

        #endregion

        #region Public properties

        /// <summary>Gets or sets the minimum of a continuous uniform distribution.</summary>
        public double Min
        {
            get { return _min; }
            set
            {
                _min = Math.Min(value, _max);
                _max = Math.Max(value, _max);
                _range = _max - _min;
            }
        }

        /// <summary>Gets or sets the maximum of a continuous uniform distribution.</summary>
        public double Max
        {
            get { return _max; }
            set
            {
                _min = Math.Min(_min, value);
                _max = Math.Max(_min, value);
                _range = _max - _min;
            }
        }

        /// <summary>Gets the range of a continuous uniform distribution.</summary>
        public IRange<double> Domain { get { return new Range<double>(_min, _max); } }

        /// <summary>Gets the mean of a continuous uniform distribution.</summary>
        public double Mean { get { return (_min + _max) / 2.0; } }

        /// <summary>Gets the median of a continuous uniform distribution.</summary>
        public double Median { get { return (_min + _max) / 2.0; } }

        /// <summary>Gets the mode of a continuous uniform distribution.</summary>
        public double Mode { get { throw new NotImplementedException(); } }

        /// <summary>Gets the variance of a continuous uniform distribution.</summary>
        public double Variance { get { return Math.Pow(_range, 2) / 12.0; } }

        /// <summary>Gets the skewness of a continuous uniform distribution.</summary>
        public double Skewness { get { return 0; } }

        /// <summary>Gets the kurtosis of a continuous uniform distribution.</summary>
        public double Kurtosis { get { return 1.8; } }

        /// <summary>Gets the entropy of a continuous uniform distribution.</summary>
        public double Entropy { get { return Math.Log(_range); } }

        #endregion

        #region Constructor

        /// <summary>
        /// Instantiates a continuous uniform distribution.
        /// </summary>
        /// <param name="min">minimum</param>
        /// <param name="max">maximum</param>
        public ContinuousUniformDistribution(double min, double max)
        {
            _min = Math.Min(min, max);
            _max = Math.Max(min, max);
            _range = _max - _min;
        }

        #endregion

        #region ICloneable implementation

        public ContinuousUniformDistribution Clone()
        {
            return new ContinuousUniformDistribution(_min, _max);
        }

        object ICloneable.Clone()
        {
            return Clone();
        }
        
        #endregion

        #region Public methods

        /// <summary>
        /// Returns the continuous uniform density given value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns>continuous uniform density</returns>
        public double Pdf(double value)
        {
            return _min <= value && value <= _max ? 1 / _range : 0;
        }

        /// <summary>
        /// Returns the cummulative continuous uniform density given value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns>cummulative continuous uniform density</returns>
        public double Cdf(double value)
        {
            if (value < _min)
                return 0;
            
            if (_min <= value && value < _max)
                return (value - _min) / _range;
            
            return 1;
        }

        public double Quantile(double probability)
        {
            return probability * _range + _min;
        }

        /// <summary>
        /// Returns a random value from the continuous uniform distribution.
        /// </summary>
        /// <returns>random value</returns>
        public double Sample(Random r)
        {
            return _min + r.NextDouble() * _range;
        }

        /// <summary>
        /// Estimates parameters of the continuous uniform distribution using maximum likelihood.
        /// <see>http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)#Estimation_of__maximum</see>
        /// </summary>
        public void MaximumLikelihoodEstimate(IEnumerable<double> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var list = source.ToList();
            Min = list.Min();
            Max = list.Max();
        }

        /// <summary>
        /// Estimates parameters of the continuous uniform distribution using Bayesian methods.
        /// </summary>
        public void BayesianEstimate(IEnumerable<double> source, params dynamic[] priorParameters)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEquatable<T> implementation

        public bool Equals(ContinuousUniformDistribution other)
        {
            return _max.Equals(other._max) & _min.Equals(other._min);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return Min.GetHashCode() ^ Max.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is ContinuousUniformDistribution))
                throw new InvalidCastException("The 'other' argument is not a ContinuousUniformDistribution object.");
            
            return Equals(other as ContinuousUniformDistribution);
        }

        public override string ToString()
        {
            return String.Format("Uniform(min = {0:G4}, max = {1:G4})", _min, _max);
        }

        #endregion
    }
}
